package cc.geeksearch.ble

import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.text.TextUtils
import androidx.core.app.ActivityCompat
import androidx.lifecycle.MutableLiveData
import cc.geeksearch.ble.bean.CharacteristicBean
import cc.geeksearch.ble.bean.DeviceBean
import cc.geeksearch.ble.bean.LogBean
import cc.geeksearch.ble.bean.ServiceBean
import java.util.Date
import java.util.UUID

class WalleConnector(private val context: Context) {
    private val TAG = "WalleConnector"
    private var bluetoothGattCallback: BluetoothGattCallback? = null
    private val gattMap = HashMap<String, BluetoothGatt>()
    private val deviceList = mutableListOf<DeviceBean>()
    private var mBluetoothAdapter: BluetoothAdapter? = null
    private val uuidMap = mutableMapOf<String, MutableList<ServiceBean>>()

    init {
        val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
        mBluetoothAdapter = manager!!.adapter
        if (mBluetoothAdapter == null) {
            WalleLog.e(TAG, "设备无可用蓝牙")
        } else if (!mBluetoothAdapter!!.isEnabled) {
            WalleLog.e(TAG, "设备蓝牙未打开")
        }
    }

    fun connect(bluetoothDevice: BluetoothDevice) {
        if (gattMap.contains(bluetoothDevice.address)) {
            WalleLog.w(TAG, "设备已连接,无需重复连接")
            return
        }
        WalleLog.d(TAG, "开始连接: ${bluetoothDevice.address}")
        if (bluetoothGattCallback == null) {
            initBluetoothGattCallback()
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (ActivityCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                WalleLog.w(TAG, "没有BLUETOOTH_CONNECT蓝牙连接权限")
                return
            }
        }
        updateDevice(
            DeviceBean(
                bluetoothDevice.address,
                bluetoothDevice.name,
                DeviceStatus.CONNECTING,
            )
        )
        val gatt = bluetoothDevice.connectGatt(
            context,
            false,
            bluetoothGattCallback,
            BluetoothDevice.TRANSPORT_LE
        )
        gattMap[bluetoothDevice.address] = gatt
    }

    private fun initBluetoothGattCallback() {
        WalleLog.d(TAG, "初始化BluetoothGattCallback")
        bluetoothGattCallback = object : BluetoothGattCallback() {
            override fun onConnectionStateChange(
                gatt: BluetoothGatt?,
                status: Int,
                newState: Int
            ) {
                super.onConnectionStateChange(gatt, status, newState)
                if (gatt == null) {
                    return
                }
                val address = if (gatt.device.address != null) gatt.device.address else ""
                WalleLog.d(
                    TAG,
                    "onConnectionStateChange address:$address status: $status, newState: $newState"
                )
                var name = ""
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    if (ActivityCompat.checkSelfPermission(
                            context,
                            Manifest.permission.BLUETOOTH_CONNECT
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        name = gatt.device.name
                    }
                } else {
                    name = gatt.device.name
                }
                when (newState) {
                    BluetoothProfile.STATE_CONNECTED -> {
                        WalleLog.i(TAG, "设备连接成功 $address")
                        val deviceItem = DeviceBean(
                            address,
                            name,
                            DeviceStatus.CONNECTED
                        )
                        updateDevice(deviceItem)
                        WalleDeviceSp(context).updateDevices(devices = deviceList)
                        gatt.discoverServices()
                    }

                    BluetoothProfile.STATE_DISCONNECTED -> {
                        WalleLog.w(TAG, "设备断开连接 $address")
                        gattMap.remove(address)
                        val deviceItem = DeviceBean(
                            address,
                            name,
                            DeviceStatus.DISCONNECTED
                        )
                        updateDevice(deviceItem)
                    }

                    BluetoothProfile.STATE_CONNECTING -> {
                        WalleLog.i(TAG, "设备正在连接 $address")
                        val deviceItem = DeviceBean(
                            address,
                            name,
                            DeviceStatus.CONNECTING
                        )
                        updateDevice(deviceItem)
                    }
                }
            }

            // 发现远程设备提供的服务及其特征和描述符
            override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
                super.onServicesDiscovered(gatt, status)
                WalleLog.d(TAG, "onServicesDiscovered status: $status")
                if (gatt == null || gatt.device == null || gatt.device.address == null) {
                    return
                }
                val address = gatt.device.address
                var name = ""
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    if (ActivityCompat.checkSelfPermission(
                            context,
                            Manifest.permission.BLUETOOTH_CONNECT
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
                        name = gatt.device.name
                    }
                } else {
                    name = gatt.device.name
                }
                when (status) {
                    BluetoothGatt.GATT_SUCCESS -> {
                        WalleLog.d(TAG, "onServicesDiscovered success")
                        gattMap[address] = gatt
                        val deviceItem = DeviceBean(
                            address,
                            name,
                            DeviceStatus.PREPARED
                        )
                        updateDevice(deviceItem)
                    }
                }
            }

            override fun onCharacteristicRead(
                gatt: BluetoothGatt,
                characteristic: BluetoothGattCharacteristic,
                value: ByteArray,
                status: Int
            ) {
                // super.onCharacteristicRead(gatt, characteristic, value, status)
                WalleLog.d(TAG, "onCharacteristicRead status: $status")
            }

            override fun onCharacteristicChanged(
                gatt: BluetoothGatt,
                characteristic: BluetoothGattCharacteristic,
                value: ByteArray
            ) {
                // 转换为十六进制字符串
                val hexString = value.fold(StringBuilder()) { sb, byte ->
                    sb.append(String.format("%02X ", byte))
                }.toString()
                WalleLog.d(TAG, "onCharacteristicChanged value: $hexString")
                if (WalleConfig.PRINT_DATA_LOG) {
                    LiveDataBus.dataLogData.postValue(
                        LogBean(
                            gatt.device.address,
                            Date(),
                            hexString
                        )
                    )
                }
                // super.onCharacteristicChanged(gatt, characteristic, value)
            }

            override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
                //super.onMtuChanged(gatt, mtu, status)
                WalleLog.d(TAG, "onMtuChanged mtu: $mtu")
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    if (ActivityCompat.checkSelfPermission(
                            context,
                            Manifest.permission.BLUETOOTH_CONNECT
                        ) != PackageManager.PERMISSION_GRANTED
                    ) {
                        return
                    }
                }
                gatt!!.requestMtu(247)
            }

            override fun onServiceChanged(gatt: BluetoothGatt) {
                // super.onServiceChanged(gatt)
                WalleLog.d(TAG, "onServiceChanged")
            }

            override fun onDescriptorWrite(
                gatt: BluetoothGatt?,
                descriptor: BluetoothGattDescriptor?,
                status: Int
            ) {
                // super.onDescriptorWrite(gatt, descriptor, status)
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    WalleLog.i(TAG, "Descriptor write successful")
                } else {
                    WalleLog.w(TAG, "Descriptor write failed: " + status)
                }
            }

            override fun onDescriptorRead(
                gatt: BluetoothGatt,
                descriptor: BluetoothGattDescriptor,
                status: Int,
                value: ByteArray
            ) {
                // super.onDescriptorRead(gatt, descriptor, status, value)
                WalleLog.d(TAG, "onDescriptorRead")
            }

            override fun onReliableWriteCompleted(gatt: BluetoothGatt?, status: Int) {
                // super.onReliableWriteCompleted(gatt, status)
                WalleLog.d(TAG, "onReliableWriteCompleted")
            }

            override fun onCharacteristicWrite(
                gatt: BluetoothGatt?,
                characteristic: BluetoothGattCharacteristic?,
                status: Int
            ) {
                //  super.onCharacteristicWrite(gatt, characteristic, status)
                WalleLog.d(TAG, "onCharacteristicWrite")
            }

            override fun onPhyRead(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
                // super.onPhyRead(gatt, txPhy, rxPhy, status)
                WalleLog.d(TAG, "onPhyRead")
            }

            override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
                //  super.onReadRemoteRssi(gatt, rssi, status)
                WalleLog.d(TAG, "onReadRemoteRssi")
            }

            override fun onPhyUpdate(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
                //   super.onPhyUpdate(gatt, txPhy, rxPhy, status)
                WalleLog.d(TAG, "onPhyUpdate")
            }
        }
    }

    fun disconnect(address: String) {
        if (TextUtils.isEmpty(address)) {
            return
        }
        val bluetoothGatt = gattMap[address] ?: return
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (ActivityCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                WalleLog.e(TAG, "断开设备（$address）失败，无BLUETOOTH_CONNECT权限")
                return
            }
        }
        bluetoothGatt.disconnect()
    }

    fun updateDevice(deviceItem: DeviceBean) {
        LiveDataBus.deviceStatusLiveData.postValue(deviceItem)
        WalleLog.d(TAG, "updateDevice: ${deviceItem.address} ${deviceItem.name}")
        when (deviceItem.status) {
            DeviceStatus.CONNECTING, DeviceStatus.CONNECTED, DeviceStatus.PREPARED, DeviceStatus.DISCONNECTED, DeviceStatus.CONNECT_FAILED -> {
                deviceList.indexOfFirst { it.address == deviceItem.address }.let {
                    if (it > -1) {
                        if (deviceItem.name.isEmpty()) {
                            deviceItem.name = deviceList[it].name
                        }
                        if(deviceItem.remark.isEmpty()){
                            deviceItem.remark = deviceList[it].remark
                        }
                        deviceList[it] = deviceItem
                    } else {
                        deviceList.add(deviceItem)
                    }
                }
            }
            else -> {
                WalleLog.w(TAG, "未知的DeviceStatus: ${deviceItem.status}")
                return
            }
        }
        LiveDataBus.devicesLiveData.postValue(deviceList)
    }

    fun getDevices() {
        if (deviceList.isEmpty()) {
            deviceList.addAll(WalleDeviceSp(context).getDevices())
        }
        LiveDataBus.devicesLiveData.postValue(deviceList)
    }

    fun delete(address: String) {
        deviceList.removeIf { it.address == address }
        gattMap.remove(address)
        uuidMap.remove(address)
        WalleDeviceSp(context).updateDevices(devices = deviceList)
        disconnect(address)
        LiveDataBus.devicesLiveData.postValue(deviceList)
    }

    fun remark(address: String, remark:String){
        deviceList.indexOfFirst { it.address == address }.let {
            if (it > -1) {
                updateDevice(DeviceBean(address, deviceList[it].name, deviceList[it].status, remark, 0))
            }
        }
    }

    fun deleteAll() {
        deviceList.clear()
        gattMap.clear()
        uuidMap.clear()
        WalleDeviceSp(context).deleteAllDevices()
        LiveDataBus.devicesLiveData.postValue(deviceList)
    }

    fun getServiceUUIDS(address: String) {
        val uuid = uuidMap[address]
        if (uuid != null) {
            LiveDataBus.uuidLiveData.value = uuidMap
            return
        }
        gattMap[address]?.let { gatt ->
            if (gatt.services != null) {
                // val serviceMap = mutableMapOf<String, MutableList<ServiceBean>>()
                val serviceList = mutableListOf<ServiceBean>()
                WalleLog.d(TAG, "------------------------- UUID Begin -------------------------")
                gatt.services.forEach { serviceItem ->
                    val serviceUUID = serviceItem.uuid.toString().uppercase()
                    WalleLog.d(TAG, serviceUUID)
                    val characteristicList = mutableListOf<CharacteristicBean>()
                    serviceItem.characteristics.forEach { characteristic ->
                        val characteristicUUID = characteristic.uuid.toString().uppercase()
                        WalleLog.d(TAG, "  $characteristicUUID")
                        val properties = characteristic.properties
                        val isReadable =
                            properties and BluetoothGattCharacteristic.PROPERTY_READ != 0
                        val isWritable =
                            properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0
                        val isNotifiable =
                            properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0
                        characteristicList.add(
                            CharacteristicBean(
                                characteristicUUID,
                                isReadable,
                                isWritable,
                                isNotifiable,
                                false,
                                characteristic.descriptors,
                            )
                        )
                        /*characteristic.descriptors.forEach { descriptor ->
                            val descriptorUUID = descriptor.uuid.toString().uppercase()
                            WalleLog.d("    $descriptorUUID")
                        }*/

                    }
                    serviceList.add(ServiceBean(serviceUUID, characteristicList))
                }
                WalleLog.d(TAG, "------------------------- UUID End -------------------------")
                uuidMap[address] = serviceList
                LiveDataBus.uuidLiveData.value = uuidMap
            }
        }
    }

    fun getConnected(address: String): BluetoothGatt? {
        return gattMap[address]
    }

    private fun getCharacteristic(address: String, serviceUUID: String, characteristicUUID: String):
            BluetoothGattCharacteristic? {
        val gatt = getGatt(address) ?: return null
        val service = gatt.getService(UUID.fromString(serviceUUID)) ?: run {
            WalleLog.e(TAG, "服务未找到")
            return null
        }
        val characteristic = service.getCharacteristic(UUID.fromString(characteristicUUID)) ?: run {
            WalleLog.e(TAG, "特征值未找到")
            return null
        }
        return characteristic;
    }

    private fun getGatt(address: String): BluetoothGatt? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (ActivityCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                WalleLog.e(TAG, "订阅失败，无BLUETOOTH_CONNECT权限")
                return null
            }
        }
        val gatt = gattMap[address]
        if (gatt == null) {
            WalleLog.e(TAG, "Gatt未找到: $address")
            return null
        }
        return gatt
    }

    /**
     * 发送数据
     * @param address 设备地址
     * @param serviceUUID 服务UUID
     * @param characteristicUUID 特征值UUID
     * @param data 数据
     */
    fun sendData(
        address: String,
        serviceUUID: String,
        characteristicUUID: String,
        data: ByteArray
    ) {
        val gatt = gattMap[address]
        if (gatt == null) {
            WalleLog.e(TAG, "数据发送失败，设备未连接。")
            return
        }
        val serviceGatt = gatt.getService(UUID.fromString(serviceUUID))
        val characteristic = serviceGatt.getCharacteristic(UUID.fromString(characteristicUUID))
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ActivityCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                WalleLog.e(TAG, "写入数据失败，无BLUETOOTH_CONNECT权限")
                return
            }
            gatt.writeCharacteristic(
                characteristic,
                data,
                BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
            )
        } else {
            characteristic.value = data
            gatt.writeCharacteristic(characteristic)
        }
    }


    /**
     * 订阅特征值变化
     * @param address 设备地址
     * @param serviceUUID 服务UUID
     * @param characteristicUUID 特征值UUID
     */
    @SuppressLint("MissingPermission")
    fun subscribe(
        address: String,
        serviceUUID: String,
        characteristicUUID: String
    ) {
        WalleLog.d(TAG, "serviceUUID：$serviceUUID characteristicUUID：$characteristicUUID")
        val gatt = getGatt(address) ?: return
        val characteristic = getCharacteristic(address, serviceUUID, characteristicUUID) ?: return

        if (characteristic.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY == 0) {
            WalleLog.w(TAG, "特征值不支持通知：$characteristicUUID")
            return
        }
        // 检查特征是否支持通知
        if (characteristic.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY == 0) {
            WalleLog.e(TAG, "特征值不支持通知")
            return
        }
        // 启用通知
        if (!gatt.setCharacteristicNotification(characteristic, true)) {
            WalleLog.e(TAG, "设置通知失败")
            return
        }
        characteristic.descriptors.forEach { descriptor ->
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    gatt.writeDescriptor(
                        descriptor,
                        BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                    )
                } else {
                    descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                    gatt.writeDescriptor(descriptor)
                }
                WalleLog.d(TAG, "订阅请求已发送")
                gatt.readDescriptor(descriptor)
            } catch (e: Exception) {
                WalleLog.e(TAG, "订阅失败: ${e.message}")
            }
            WalleLog.d(TAG, "订阅成功：$characteristicUUID")
        }
        val serviceList = uuidMap[address] ?: return
        val serviceIndex = serviceList.indexOfFirst { it.uuid == serviceUUID }
        if (serviceIndex == -1) {
            WalleLog.e(TAG, "服务未找到 $serviceUUID")
            return
        }
        val characteristicIndex =
            serviceList[serviceIndex].characteristics.indexOfFirst { it.uuid == characteristicUUID }
        if (characteristicIndex == -1) {
            WalleLog.e(TAG, "特征值未找到 $characteristicUUID")
            return
        }
        serviceList[serviceIndex].characteristics[characteristicIndex].subscribed = true
        WalleLog.d(TAG, "订阅成功2")
        LiveDataBus.uuidLiveData.value = uuidMap
    }

    /**
     * 取消订阅特征值变化
     * @param address 设备地址
     * @param serviceUUID 服务UUID
     * @param characteristicUUID 特征值UUID
     */
    @SuppressLint("MissingPermission")
    fun unsubscribe(
        address: String,
        serviceUUID: String,
        characteristicUUID: String
    ) {
        WalleLog.d(
            TAG,
            "unsubscribe serviceUUID：$serviceUUID characteristicUUID：$characteristicUUID"
        )
        val gatt = getGatt(address) ?: return
        val characteristic = getCharacteristic(address, serviceUUID, characteristicUUID) ?: return
        if (!gatt.setCharacteristicNotification(characteristic, false)) {
            WalleLog.e(TAG, "设置关闭通知失败")
            return
        }
        characteristic.descriptors.forEach { descriptor ->
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    gatt.writeDescriptor(
                        descriptor,
                        BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
                    )
                } else {
                    descriptor.value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
                    gatt.writeDescriptor(descriptor)
                }
                gatt.readDescriptor(descriptor)
            } catch (e: Exception) {
                WalleLog.e(TAG, "取消订阅失败: ${e.message}")
            }
        }
        val serviceList = uuidMap[address] ?: return
        serviceList.indexOfFirst { it.uuid == serviceUUID }.let { service ->
            if (service == -1) {
                WalleLog.e(TAG, "服务未找到")
            } else {
                serviceList[service].characteristics.indexOfFirst { it.uuid == characteristicUUID }
                    .let {
                        if (it == -1) {
                            WalleLog.e(TAG, "特征值未找到")
                        } else {
                            serviceList[it].characteristics[it].subscribed = false
                        }
                    }
            }
        }
        LiveDataBus.uuidLiveData.value = uuidMap
    }

    @SuppressLint("MissingPermission")
    fun read(address: String, serviceUUID: String, characteristicUUID: String) {
        val gatt = getGatt(address) ?: return
        val characteristic = getCharacteristic(address, serviceUUID, characteristicUUID) ?: return
        gatt.readCharacteristic(characteristic)
    }
}